﻿#region "Copyright (C) Lenny Granovsky. 2005-2013"
//This program is free software distribute under the terms of the GNU General Public License as published by
//the Free Software Foundation. Please read AssemblyInfo.cs file for more information.
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace Web.Enhancements.Rest
{
    /// <summary>
    /// The interface that WebRestServiceContext must implement.
    /// </summary>
    public interface IWebRestServiceContext
    {
        /// <summary>
        /// Gets the calling method reflection information.
        /// </summary>
        System.Web.Services.Protocols.LogicalMethodInfo MethodInfo { get; }
        /// <summary>
        /// Gets the list of REST service filters that current service call has.
        /// </summary>
        List<Filters.IWebRestFilter> Filters { get; }
        /// <summary>
        /// Gets an array of objects that were resolved as current service method call parameters.
        /// </summary>
        object[] MethodResolvedParameters { get; }
        /// <summary>
        /// Gets the content type that service determined to use to return data.
        /// </summary>
        string ContentType { get; }
        /// <summary>
        /// Gets the session mode the service is running at. 
        /// It is not possible to change the session mode from within the service itself at the runtime.
        /// </summary>
        System.Web.SessionState.SessionStateBehavior SessionMode { get; }
        /// <summary>
        /// Gets or sets the HTTP Content-Disposition header that response will write to the stream upon method completion.
        /// </summary>
        string ContentDisposition { get; set; }
        /// <summary>
        /// Gets boolean value indicated if method is configured to use OutputCache.
        /// </summary>
        bool IsOutputCacheInUse { get; }
        /// <summary>
        /// Gets or sets the JSON serializer type when the JSON is used as output. 
        /// The default value is standard JavaScriptSerializer.
        /// </summary>
        JsonSerializerType JsonSerializer { get; set; }
        /// <summary>
        /// Gets or sets indication that HTTP request processing should be cancelled. 
        /// Cancellation may be requested after any filter or actual method execution.
        /// </summary>
        /// <remarks>If processing is cancelled no changes to the response is being made. 
        /// as well as no changes to the HTTP pipeline processing is applied; 
        /// all subsequent events within HTTP pipeline are following.</remarks>
        bool Cancel { get; set; }

        /// <summary>
        /// Sets the HTTP status code and description to the outgoing Web response.
        /// </summary>
        /// <param name="statusCode">HTTP Status code.</param>
        /// <param name="statusDescription">Custom description for the status.</param>
        void SetStatus(int statusCode, string statusDescription);
        /// <summary>
        /// Sets the HTTP status code and description to the outgoing Web response.
        /// </summary>
        /// <param name="statusCode">HTTP Status code.</param>
        /// <param name="statusDescription">Custom description for the status.</param>
        void SetStatus(System.Net.HttpStatusCode statusCode, string statusDescription);
        /// <summary>
        /// Sets the HTTP status code of the outgoing Web response to Created and sets the Location header to the provided URI.
        /// </summary>
        void SetStatusAsCreated();
        /// <summary>
        /// Sets the HTTP status code of the outgoing Web response to NotFound.
        /// </summary>
        void SetStatusAsNotFound();
        /// <summary>
        /// Sets the HTTP status code of the outgoing Web response to NotFound with the specified description.
        /// </summary>
        /// <param name="statusDescription">Custom description for the status.</param>
        void SetStatusAsNotFound(string statusDescription);
    }

    /// <summary>
    /// This class holds all runtime information for the REST service.
    /// </summary>
    public class WebRestServiceContext : IWebRestServiceContext
    {
        private const string memorySpotName = "CurrentWebRestServiceContext";

        /// <summary>
        /// Gets an instance of the WebRestServiceContext for the current request.
        /// </summary>
        public static WebRestServiceContext Current
        {
            get
            {
                if (HttpContext.Current == null)
                    return null;
                if (HttpContext.Current.Items[memorySpotName] == null)
                    return null;
                return (WebRestServiceContext)HttpContext.Current.Items[memorySpotName];
            }
        }

        /// <summary>
        /// Gets the calling method reflection information.
        /// </summary>
        public System.Web.Services.Protocols.LogicalMethodInfo MethodInfo { get; internal set; }
        /// <summary>
        /// Gets the list of REST service filters that current service call has.
        /// </summary>
        public List<Filters.IWebRestFilter> Filters { get; internal set; }
        /// <summary>
        /// Gets an array of objects that were resolved as current service method call parameters.
        /// </summary>
        public object[] MethodResolvedParameters { get; internal set; }
        /// <summary>
        /// Gets the content type that service determined to use to return data.
        /// </summary>
        public string ContentType { get; set; }
        /// <summary>
        /// Gets or sets the HTTP Content-Disposition header that response will write to the stream upon method completion.
        /// </summary>
        public string ContentDisposition { get; set; }
        /// <summary>
        /// Gets the calling method internally cached info for the REST service runtime.
        /// </summary>
        internal InfoCaching.MethodInfoCacheItem MethodInfoCache { get; set; }
        /// <summary>
        /// Gets the session mode the service is running at. 
        /// It is not possible to change the session mode from within the service itself at the runtime.
        /// </summary>
        public System.Web.SessionState.SessionStateBehavior SessionMode { get; internal set; }
        /// <summary>
        /// Gets or sets the JSON serializer type when the JSON is used as output. 
        /// The default value is standard JavaScriptSerializer.
        /// </summary>
        public JsonSerializerType JsonSerializer { get; set; }
        /// <summary>
        /// Gets boolean value indicated if method is configured to use OutputCache.
        /// </summary>
        public bool IsOutputCacheInUse
        {
            get
            {
                return (this.MethodInfoCache.CacheParameters.Enabled && this.MethodInfoCache.CacheParameters.Duration > 0);
            }
        }
        /// <summary>
        /// Gets or sets indication that HTTP request processing should be cancelled. 
        /// Cancellation may be requested after any filter or actual method execution.
        /// </summary>
        /// <remarks>If processing is cancelled no changes to the response is being made. 
        /// as well as no changes to the HTTP pipeline processing is applied; 
        /// all subsequent events within HTTP pipeline are following.</remarks>
        public bool Cancel { get; set; }

        /// <summary>
        /// Default constructor.
        /// </summary>
        internal WebRestServiceContext()
        {
            this.ContentType = "text/html";
            this.ContentDisposition = "";
            this.Filters = new List<Filters.IWebRestFilter>();
            this.SessionMode = System.Web.SessionState.SessionStateBehavior.Default;
            this.JsonSerializer = JsonSerializerType.JavaScriptSerializer;
        }

        /// <summary>
        /// Method creates an instance of WebRestServiceContext for the current service request.
        /// </summary>
        internal static void CreateCurrentInstance()
        {
            if (HttpContext.Current == null)
                throw new WebRestException(ErrorMessages.ResourceManager.GetString("ServiceContextCannotInitNoHttpContext"));
            HttpContext.Current.Items[memorySpotName] = new WebRestServiceContext();
        }

        /// <summary>
        /// Sets the HTTP status code of the outgoing Web response to Created and sets the Location header to the provided URI.
        /// </summary>
        public void SetStatusAsCreated()
        {
            this.SetStatus(System.Net.HttpStatusCode.Created, "Created");
        }

        /// <summary>
        /// Sets the HTTP status code of the outgoing Web response to NotFound.
        /// </summary>
        public void SetStatusAsNotFound()
        {
            this.SetStatus(System.Net.HttpStatusCode.NotFound, "Not Found");
        }

        /// <summary>
        /// Sets the HTTP status code of the outgoing Web response to NotFound with the specified description.
        /// </summary>
        /// <param name="statusDescription">Custom description for the status.</param>
        public void SetStatusAsNotFound(string statusDescription)
        {
            if (string.IsNullOrWhiteSpace(statusDescription))
                this.SetStatusAsNotFound();
            else
                this.SetStatus(System.Net.HttpStatusCode.NotFound, statusDescription);
        }

        /// <summary>
        /// Sets the HTTP status code and description to the outgoing Web response.
        /// </summary>
        /// <param name="statusCode">HTTP Status code.</param>
        /// <param name="statusDescription">Custom description for the status.</param>
        public void SetStatus(int statusCode, string statusDescription)
        {
            if (statusDescription == null)
                statusDescription = statusCode.ToString();
            if (HttpContext.Current != null)
                HttpContext.Current.Response.Status = string.Format("{0} {1}", statusCode.ToString(), statusDescription);
        }

        /// <summary>
        /// Sets the HTTP status code and description to the outgoing Web response.
        /// </summary>
        /// <param name="statusCode">HTTP Status code.</param>
        /// <param name="statusDescription">Custom description for the status.</param>
        public void SetStatus(System.Net.HttpStatusCode statusCode, string statusDescription)
        {
            this.SetStatus((int)statusCode, statusDescription);
        }
    }
}
